﻿// 被监控端_线程版.cpp : 定义应用程序的入口点。
//

#include "pch.h"
#include "framework.h"
#include "被监控端_线程版.h"

#include <stdio.h>

#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")



//前后两张图片的发送间隔   ms
#define TIME_SPACE 50


//服务器ip地址：
#define SERVER_ADDR  "10.133.98.104"
//服务器端口号：
#define  SERVER_PORT 9527
//图片文件名：
#define BMP_FILE_NAME "caption.bmp"
//图形文件大小：
#define BMP_FILE_SIZE 6220868

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

SOCKET g_sock;

// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);


void OnCreate(HWND hWnd);

void thread_fun(HWND hWnd);

//获取整个桌面：
void getAndSend(HWND hWnd);

//把hbitmap转存到文件中
bool SaveBitmapToFile(HBITMAP hBitmap, char* filename);

//计算位图文件每个像素所占字节数
WORD GetBitmapBitCount();

//处理调色板
void ProcessPalette(HBITMAP hBitmap, const BITMAP& bitmap,
    DWORD paletteSize, LPBITMAPINFOHEADER lpBmpInfoHeader);




int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MY, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int)msg.wParam;
}



//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MY);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // 将实例句柄存储在全局变量中

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_TIMER:
        getAndSend(hWnd);

        break;
    case WM_CREATE:

        OnCreate(hWnd);

        break;
    case WM_COMMAND:
    {
        int wmId = LOWORD(wParam);
        // 分析菜单选择:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: 在此处添加使用 hdc 的任何绘图代码...
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        KillTimer(hWnd, 10086);
        closesocket(g_sock);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

void OnCreate(HWND hWnd)
{
    //tcp网络连接：
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);

    g_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    SOCKADDR_IN sAddr = { 0 };
    sAddr.sin_family = AF_INET;
    sAddr.sin_addr.S_un.S_addr = inet_addr(SERVER_ADDR);
    sAddr.sin_port = htons(SERVER_PORT);

    connect(g_sock, (sockaddr*)&sAddr, sizeof(sAddr));

    //getAndSend(hWnd);

    //SetTimer(hWnd, 10086, TIME_SPACE, NULL);

    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)thread_fun, hWnd, NULL, NULL);
}


void thread_fun(HWND hWnd)
{
    while (1)
    {
        //1.获取桌面截屏：
        HDC dskDC = CreateDC("DISPLAY", NULL, NULL, NULL);
        int width = GetDeviceCaps(dskDC, HORZRES);
        int height = GetDeviceCaps(dskDC, VERTRES);

        HDC comDC = CreateCompatibleDC(dskDC);
        HBITMAP bmp = CreateCompatibleBitmap(dskDC, width, height);
        SelectObject(comDC, bmp);

        BitBlt(comDC, 0, 0, width, height,
            dskDC, 0, 0, SRCCOPY);

        //2.存储成图片：
        SaveBitmapToFile(bmp, BMP_FILE_NAME);


        //3.发送图片到服务器：

        //打开文件：
        FILE* fp = fopen(BMP_FILE_NAME, "rb");

        //文件大小：
        int fileSize = BMP_FILE_SIZE;

        //发送文件内容：
        char temp[4096];
        int r;
        while (1)
        {
            r = fread(temp, 1, 4096, fp);
            if (r <= 0)
            {
                break;
            }
            send(g_sock, temp, r, NULL);

        }


        //关闭文件：
        fclose(fp);

        Sleep(TIME_SPACE);
    }
}
//获取整个桌面：
void getAndSend(HWND hWnd)
{
    //1.获取桌面截屏：
    HDC dskDC = CreateDC("DISPLAY", NULL, NULL, NULL);
    int width = GetDeviceCaps(dskDC, HORZRES);
    int height = GetDeviceCaps(dskDC, VERTRES);

    HDC comDC = CreateCompatibleDC(dskDC);
    HBITMAP bmp = CreateCompatibleBitmap(dskDC, width, height);
    SelectObject(comDC, bmp);

    BitBlt(comDC, 0, 0, width, height,
        dskDC, 0, 0, SRCCOPY);

    //2.存储成图片：
    SaveBitmapToFile(bmp, BMP_FILE_NAME);


    //3.发送图片到服务器：

    //打开文件：
    FILE* fp = fopen(BMP_FILE_NAME, "rb");

    //文件大小：
    int fileSize = BMP_FILE_SIZE;

    //发送文件内容：
    char temp[4096];
    int r;
    while (1)
    {
        r = fread(temp, 1, 4096, fp);
        if (r <= 0)
        {
            break;
        }
        send(g_sock, temp, r, NULL);

    }


    //关闭文件：
    fclose(fp);

}

//把hbitmap转存到文件中
bool SaveBitmapToFile(HBITMAP hBitmap, char* filename)
{
    //1. 创建位图文件
    HANDLE file = CreateFile(filename, GENERIC_WRITE,
        0, nullptr, CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
        nullptr);
    if (file == INVALID_HANDLE_VALUE)
    {
        return false;
    }

    //2. 计算位图文件每个像素所占字节数
    WORD bitCount = GetBitmapBitCount();

    //3. 获取位图结构
    BITMAP bitmap;
    ::GetObject(hBitmap, sizeof(bitmap), reinterpret_cast<LPSTR>(&bitmap));

    //位图中像素字节大小(32字节对齐)
    const DWORD bmBitsSize = ((bitmap.bmWidth * bitCount + 31) / 32) * 4 * bitmap.bmHeight;

    //调色板大小
    const DWORD paletteSize = 0;

    //4. 构造位图信息头
    BITMAPINFOHEADER  bmpInfoHeader; //位图信息头结构
    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfoHeader.biWidth = bitmap.bmWidth;
    bmpInfoHeader.biHeight = bitmap.bmHeight;
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = bitCount;
    bmpInfoHeader.biCompression = BI_RGB;
    bmpInfoHeader.biSizeImage = 0;
    bmpInfoHeader.biXPelsPerMeter = 0;
    bmpInfoHeader.biYPelsPerMeter = 0;
    bmpInfoHeader.biClrImportant = 0;
    bmpInfoHeader.biClrUsed = 0;

    //5. 构造位图文件头
    BITMAPFILEHEADER bmpFileHeader;
    bmpFileHeader.bfType = 0x4D42; //"BM"
    //位图文件大小
    const DWORD dibSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paletteSize + bmBitsSize;
    bmpFileHeader.bfSize = dibSize;
    bmpFileHeader.bfReserved1 = 0;
    bmpFileHeader.bfReserved2 = 0;
    bmpFileHeader.bfOffBits = static_cast<DWORD>(sizeof(BITMAPFILEHEADER))
        + static_cast<DWORD>(sizeof(BITMAPINFOHEADER)) + paletteSize;

    //6. 为位图内容分配内存 
    const auto dib = GlobalAlloc(GHND, bmBitsSize + paletteSize + sizeof(BITMAPINFOHEADER)); //内存句柄
    const auto lpBmpInfoHeader = static_cast<LPBITMAPINFOHEADER>(GlobalLock(dib)); //指向位图信息头结构
    *lpBmpInfoHeader = bmpInfoHeader;

    //7. 处理调色板
    ProcessPalette(hBitmap, bitmap, paletteSize, lpBmpInfoHeader);

    //8. 写入文件
    DWORD written = 0; //写入文件字节数   
    WriteFile(file, reinterpret_cast<LPSTR>(&bmpFileHeader), sizeof(BITMAPFILEHEADER),
        &written, nullptr); //写入位图文件头
    WriteFile(file, reinterpret_cast<LPSTR>(lpBmpInfoHeader), dibSize,
        &written, nullptr); //写入位图文件其余内容

    //9. 清理资源
    GlobalUnlock(dib);
    GlobalFree(dib);
    CloseHandle(file);

    return true;
}

//计算位图文件每个像素所占字节数
WORD GetBitmapBitCount()
{
    const auto dc = ::CreateDCA("DISPLAY", nullptr, nullptr, nullptr);
    //当前分辨率下每像素所占字节数
    const auto bits = ::GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES);
    ::DeleteDC(dc);

    //位图中每像素所占字节数
    WORD bitCount;
    if (bits <= 1)
        bitCount = 1;
    else if (bits <= 4)
        bitCount = 4;
    else if (bits <= 8)
        bitCount = 8;
    else
        bitCount = 24;

    return bitCount;
}

//处理调色板
void ProcessPalette(HBITMAP hBitmap, const BITMAP& bitmap,
    DWORD paletteSize, LPBITMAPINFOHEADER lpBmpInfoHeader)
{
    HANDLE oldPalette = nullptr;
    HDC dc = nullptr;
    const auto palette = GetStockObject(DEFAULT_PALETTE);
    if (palette != nullptr)
    {
        dc = ::GetDC(nullptr);
        oldPalette = ::SelectPalette(dc, static_cast<HPALETTE>(palette), FALSE);
        ::RealizePalette(dc); //实现设备调色板
    }

    //获取该调色板下新的像素值
    GetDIBits(dc, hBitmap, 0, static_cast<UINT>(bitmap.bmHeight),
        reinterpret_cast<LPSTR>(lpBmpInfoHeader) + sizeof(BITMAPINFOHEADER) + paletteSize,
        reinterpret_cast<BITMAPINFO*>(lpBmpInfoHeader), DIB_RGB_COLORS);

    //恢复调色板
    if (oldPalette != nullptr)
    {
        ::SelectPalette(dc, static_cast<HPALETTE>(oldPalette), TRUE);
        ::RealizePalette(dc);
        ::ReleaseDC(nullptr, dc);
    }
}